package net.sureon.biz;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import net.sureon.common.cache.InCompanyCacheProvider;
import net.sureon.common.cache.LRUCache;
import net.sureon.common.exception.ExceptionKeys;
import net.sureon.common.exception.SureonException;
import net.sureon.common.security.Action;
import net.sureon.dao.Group;
import net.sureon.dao.User;
import net.sureon.dao.UserDAO;
@SuppressWarnings("unchecked")
public class UserMgrImpl implements UserMgr {

	private UserDAO userDAO;
	private GroupMgr groupMgr;
	private CompanyMgr companyMgr;

	public UserMgrImpl() {
	}

	/**
	 * @return the userDAO
	 */
	public UserDAO getUserDAO() {
		return userDAO;
	}

	/**
	 * @param userDAO
	 *            the userDAO to set
	 */
	public void setUserDAO(UserDAO userDAO) {
		this.userDAO = userDAO;
	}

	/**
	 * @return the groupMgr
	 */
	public GroupMgr getGroupMgr() {
		return groupMgr;
	}

	/**
	 * @return the companyMgr
	 */
	public CompanyMgr getCompanyMgr() {
		return companyMgr;
	}

	/**
	 * @param companyMgr
	 *            the companyMgr to set
	 */
	public void setCompanyMgr(CompanyMgr companyMgr) {
		this.companyMgr = companyMgr;
	}

	/**
	 * @param groupMgr
	 *            the groupMgr to set
	 */
	public void setGroupMgr(GroupMgr groupMgr) {
		this.groupMgr = groupMgr;
	}

	public UserBO getUser(long companyID, long userID) throws SureonException {
		LRUCache cache = InCompanyCacheProvider.getInstance().getUserCache(
				companyID);
		Object o = cache.get(userID);
		if (o == null) {
			User userVO = getUserDAO().getUser(companyID, userID);
			if (userVO == null) {
				throw new SureonException(ExceptionKeys.USER_NOT_FOUND)
						.appendExtraInfo("companyID", companyID)
						.appendExtraInfo("userID", userID);
			}
			Group directGroup = getDirectParentGroup(companyID, userVO
					.getGroupID(), userVO.getUserID());
			if (directGroup != null && directGroup.getGroupManager() > 0) {
				UserBO superiorUser = getUser(companyID, directGroup
						.getGroupManager());
				userVO.setSuperiorID(superiorUser.getUserID());
				userVO.setSuperior(superiorUser.getUserVO().getRealName());
			}
			if (userVO.getGroupID() > 0) {
				userVO.setGroupName(groupMgr.getGroup(companyID,
						userVO.getGroupID()).getGroupVO().getGroupName());
			}

			o = new UserBO(userVO);
			cache.put(userID, o);
		}
		return (UserBO) o;
	}

	public List<UserBO> getUserByGroupID(long companyID, long groupID)
			throws SureonException {
		LRUCache cache = InCompanyCacheProvider.getInstance().getUserCache(
				companyID);
		List<User> users = getUserDAO().getUserByGroup(companyID, groupID);
		List<UserBO> userBOs = new ArrayList<UserBO>();
		Group directGroup;
		for (User user : users) {
			directGroup = getDirectParentGroup(companyID, user.getGroupID(),
					user.getUserID());
			if (directGroup != null && directGroup.getGroupManager() > 0) {
				UserBO superiorUser = getUser(companyID, directGroup
						.getGroupManager());
				user.setSuperiorID(superiorUser.getUserID());
				user.setSuperior(superiorUser.getUserVO().getRealName());
			}
			if (user.getGroupID() > 0) {
				user.setGroupName(groupMgr.getGroup(companyID,
						user.getGroupID()).getGroupVO().getGroupName());
			}
			UserBO theBO = new UserBO(user);
			userBOs.add(theBO);
			cache.put(user.getUserID(), theBO);
		}
		return userBOs;
	}

	public List<Long> getDirectChildUsers(long companyID, long userID)
			throws SureonException {
		return getChildUsers(companyID, userID, 1);
	}

	public List<Long> getChildUsersWithConfiguredLevel(long companyID,
			long theManageUserID) throws SureonException {
		int configuredLevel = companyMgr.getCompanyConfig(companyID)
				.getVisibleDirectGroupLevel();
		return getChildUsers(companyID, theManageUserID, configuredLevel);
	}

	public List<Long> getChildUsers(long companyID, long theManageUserID,
			int level) throws SureonException {
		UserBO user = getUser(companyID, theManageUserID);
		if (Action.WriteGroup.probeAccess(user)) {
			List<Long> users = new ArrayList<Long>();
			recusiveAddChildUsers(users, companyID, user.getGroupID(), level);
			return users;
		}
		return Collections.EMPTY_LIST;
	}

	private void recusiveAddChildUsers(List<Long> users, long companyID,
			long groupID, int level) throws SureonException {
		if (level == 0) {
			return;
		}
		List<GroupBO> childGroups = groupMgr.getGroup(companyID, groupID)
				.getChildGroups();
		users.addAll(getTheGroupSameLevelUsers(companyID, groupID));
		for (GroupBO group : childGroups) {
			recusiveAddChildUsers(users, companyID, group.getGroupID(),
					level - 1);
		}
	}

	private List<Long> getTheGroupSameLevelUsers(long companyID, long groupID)
			throws SureonException {
		GroupBO group = groupMgr.getGroup(companyID, groupID);
		List<Long> result = userDAO.getUserIDByGroup(companyID, groupID, group
				.getGroupVO().getGroupManager());
		result.addAll(group.getDirectChildGroupManagers());
		return result;
	}

	public List<UserBO> getUserByCompanyID(long companyID, int skipResults,
			int maxResults) throws SureonException {
		List<UserBO> userBOs = new ArrayList<UserBO>();
		List<User> users = getUserDAO().getUserByCompany(companyID,
				skipResults, maxResults);
		Group directGroup;
		for (User user : users) {
			directGroup = getDirectParentGroup(companyID, user.getGroupID(),
					user.getUserID());
			if (directGroup != null && directGroup.getGroupManager() > 0) {
				UserBO superiorUser = getUser(companyID, directGroup
						.getGroupManager());
				user.setSuperiorID(superiorUser.getUserID());
				user.setSuperior(superiorUser.getUserVO().getRealName());
			}
			if (user.getGroupID() > 0) {
				user.setGroupName(groupMgr.getGroup(companyID,
						user.getGroupID()).getGroupVO().getGroupName());
			}
			UserBO theBO = new UserBO(user);
			userBOs.add(theBO);
		}
		return userBOs;
	}

	public UserBO getSuperiorUser(long companyID, long mySelf)
			throws SureonException {
		UserBO userBO = getUser(companyID, mySelf);
		Group directParentGroup = getDirectParentGroup(companyID, userBO
				.getGroupID(), mySelf);
		if (directParentGroup != null
				&& directParentGroup.getGroupManager() > 0) {
			return getUser(companyID, directParentGroup.getGroupManager());
		}
		return null;
	}

	private Group getDirectParentGroup(long companyID, long groupID, long mySelf)
			throws SureonException {
		if (groupID > 0) {
			Group groupVO = groupMgr.getGroup(companyID, groupID).getGroupVO();
			if (groupVO.getGroupManager() >= 0
					&& groupVO.getGroupManager() != mySelf) {
				return groupVO;
			} else {
				return getDirectParentGroup(companyID,
						groupVO.getParentGroup(), mySelf);
			}
		}
		return null;
	}

	public UserBO getUserByUsername(long companyID, String userName)
			throws SureonException {
		User u = getUserDAO().getUserByUsername(companyID, userName);
		if (u == null) {
			throw new SureonException(ExceptionKeys.USER_NOT_FOUND)
					.appendExtraInfo("companyID", companyID).appendExtraInfo(
							"userName", userName);
		}
		UserBO user = new UserBO(u);
		// InCompanyCacheProvider.getInstance().getUserCache(companyID).put(
		// user.getUserVO().getUserID(), user);
		return user;
	}

	public long insertUser(UserBO user) throws SureonException {
		long userID = getUserDAO().insertUser(user.getUserVO());
		user.getUserVO().setUserID(userID);
		InCompanyCacheProvider.getInstance().getUserCache(
				user.getUserVO().getCompanyID()).put(userID, user);
		return userID;
	}

	public int updateUser(UserBO user) throws SureonException {
		int affectedRows = getUserDAO().updateUser(user.getUserVO());
		InCompanyCacheProvider.getInstance().getUserCache(
				user.getUserVO().getCompanyID()).put(
				user.getUserVO().getUserID(), user);
		return affectedRows;
	}

	public UserBO getUserByEmail(String email) {
		User vo = userDAO.getUserByEmail(email);
		if (vo != null) {
			return new UserBO(vo);
		}
		return null;
	}

	public List<UserBO> getUserByGroup(long companyID, long groupID) {
		List<User> list = userDAO.getUserByGroup(companyID, groupID);
		List<UserBO> all = new ArrayList<UserBO>();
		UserBO userBO = null;
		for (User user : list) {
			userBO = new UserBO(user);
			all.add(userBO);
		}
		return all;
	}

	public UserBO getAdminUser(long companyID) throws SureonException {
		return new UserBO(userDAO.getAdminUser(companyID));
	}
}